namespace ITU.EtermCore.Net
{
    using System;
    using System.Collections;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading;
    using System.Timers;

    public class ClientSocket : BaseSocket
    {
        private Socket _socket;
        private const int BUFFERSIZE = 0x800;
        private System.Threading.Timer connectTimeoutTimer;
        private bool m_Compressed = false;
        private bool m_ConnectTimedOut = false;
        private Stream m_NetworkStream = null;
        private bool m_PendingSend = false;
        private byte[] m_ReadBuffer = null;
        private System.Collections.Queue m_SendQueue = new System.Collections.Queue();
        private bool m_SSL = false;
        private SslStream m_SSLStream;
        private NetworkStream m_Stream;
        private int sendnoresponseinterval = 0x1770;
        private System.Timers.Timer sendTimeoutTimer;

        public ClientSocket()
        {
            this.sendTimeoutTimer = new System.Timers.Timer((double) this.SendNoResponseInterval);
            this.sendTimeoutTimer.Elapsed += new ElapsedEventHandler(this.sendTimeoutTimer_Elapsed);
        }

        public override void Connect()
        {
            base.Connect();
            this.m_Compressed = false;
            this.m_ReadBuffer = null;
            this.m_ReadBuffer = new byte[0x800];
            try
            {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(base.Address), base.Port);
                this.m_ConnectTimedOut = false;
                TimerCallback callback = new TimerCallback(this.connectTimeoutTimerDelegate);
                this.connectTimeoutTimer = new System.Threading.Timer(callback, null, this.ConnectTimeout, this.ConnectTimeout);
                Debug.WriteLine("IPV4");
                this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this._socket.BeginConnect(remoteEP, new AsyncCallback(this.EndConnect), null);
            }
            catch (Exception exception)
            {
                base.FireOnError(exception);
            }
        }

        public void Connect(string address, int port)
        {
            base.Address = address;
            base.Port = port;
            this.Connect();
        }

        private void connectTimeoutTimerDelegate(object stateInfo)
        {
            this.connectTimeoutTimer.Dispose();
            this.m_ConnectTimedOut = true;
            this._socket.Close();
        }

        public override void Disconnect()
        {
            base.Disconnect();
            this.sendTimeoutTimer.Stop();
            if (this._socket != null)
            {
                try
                {
                    this._socket.Shutdown(SocketShutdown.Both);
                }
                catch
                {
                }
                try
                {
                    this._socket.Close();
                }
                catch
                {
                }
                base.FireOnDisconnect();
            }
        }

        private void DisplayCertificateInformation(SslStream stream)
        {
            Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
            X509Certificate remoteCertificate = stream.RemoteCertificate;
            if (stream.RemoteCertificate != null)
            {
                Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.", remoteCertificate.Subject, remoteCertificate.GetEffectiveDateString(), remoteCertificate.GetExpirationDateString());
            }
            else
            {
                Console.WriteLine("Remote certificate is null.");
            }
        }

        private void DisplaySecurityLevel(SslStream stream)
        {
            Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
            Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
            Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
            Console.WriteLine("Protocol: {0}", stream.SslProtocol);
        }

        private void DisplaySecurityServices(SslStream stream)
        {
            Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
            Console.WriteLine("IsSigned: {0}", stream.IsSigned);
            Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
        }

        private void DisplayStreamProperties(SslStream stream)
        {
            Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
            Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
        }

        private void EndConnect(IAsyncResult ar)
        {
            if (this.m_ConnectTimedOut)
            {
                base.FireOnError(new ConnectTimeoutException("Attempt to connect timed out"));
            }
            else
            {
                try
                {
                    this.connectTimeoutTimer.Dispose();
                    this._socket.EndConnect(ar);
                    this.m_Stream = new NetworkStream(this._socket, false);
                    this.m_NetworkStream = this.m_Stream;
                    if (this.m_SSL)
                    {
                        this.InitSSL();
                    }
                    base.FireOnConnect();
                    this.Receive();
                }
                catch (Exception exception)
                {
                    base.FireOnError(exception);
                }
            }
        }

        private void EndReceive(IAsyncResult ar)
        {
            try
            {
                int length = this.m_NetworkStream.EndRead(ar);
                if (length > 0)
                {
                    this.sendTimeoutTimer.Stop();
                    base.FireOnReceive(this.m_ReadBuffer, length);
                    if (this.Connected)
                    {
                        this.Receive();
                    }
                }
                else
                {
                    this.Disconnect();
                }
            }
            catch (ObjectDisposedException)
            {
            }
            catch (IOException exception)
            {
                Console.WriteLine("\nSocket Exception: " + exception.Message);
                this.Disconnect();
            }
        }

        private void EndSend(IAsyncResult ar)
        {
            lock (this)
            {
                try
                {
                    this.m_NetworkStream.EndWrite(ar);
                    if (this.m_SendQueue.Count > 0)
                    {
                        byte[] buffer = (byte[]) this.m_SendQueue.Dequeue();
                        this.m_NetworkStream.BeginWrite(buffer, 0, buffer.Length, new AsyncCallback(this.EndSend), null);
                    }
                    else
                    {
                        this.m_PendingSend = false;
                    }
                }
                catch (Exception)
                {
                    this.Disconnect();
                }
            }
        }

        private void InitSSL()
        {
            this.InitSSL(SslProtocols.Default);
        }

        private void InitSSL(SslProtocols protocol)
        {
            this.m_SSLStream = new SslStream(this.m_Stream, false, new RemoteCertificateValidationCallback(this.ValidateCertificate), null);
            try
            {
                this.m_SSLStream.AuthenticateAsClient(base.Address, null, protocol, true);
            }
            catch (AuthenticationException exception)
            {
                Console.WriteLine("Exception: {0}", exception.Message);
                if (exception.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", exception.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection.");
                return;
            }
            this.m_NetworkStream = this.m_SSLStream;
            this.m_SSL = true;
        }

        private void Receive()
        {
            this.m_NetworkStream.BeginRead(this.m_ReadBuffer, 0, 0x800, new AsyncCallback(this.EndReceive), null);
        }

        public override void Send(string data)
        {
            this.Send(Encoding.UTF8.GetBytes(data));
        }

        public override void Send(byte[] bData)
        {
            lock (this)
            {
                Exception exception;
                try
                {
                    base.FireOnSend(bData, bData.Length);
                    if (this.m_PendingSend)
                    {
                        this.m_SendQueue.Enqueue(bData);
                    }
                    else
                    {
                        this.m_PendingSend = true;
                        this.sendTimeoutTimer.Start();
                        try
                        {
                            this.m_NetworkStream.BeginWrite(bData, 0, bData.Length, new AsyncCallback(this.EndSend), null);
                        }
                        catch (Exception exception1)
                        {
                            exception = exception1;
                            this.Disconnect();
                        }
                    }
                }
                catch (Exception exception2)
                {
                    exception = exception2;
                }
            }
        }

        private void sendTimeoutTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            string s = "read timeout";
            byte[] bytes = Encoding.Default.GetBytes(s);
            byte[] array = new byte[bytes.Length + 1];
            bytes.CopyTo(array, 0);
            array[bytes.Length] = 3;
            base.FireOnReceive(array, array.Length);
            this.sendTimeoutTimer.Stop();
        }

        public override void StartTls()
        {
            base.StartTls();
            SslProtocols tls = SslProtocols.Tls;
            this.InitSSL(tls);
        }

        private bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return base.FireOnValidateCertificate(sender, certificate, chain, sslPolicyErrors);
        }

        public bool Compressed
        {
            get
            {
                return this.m_Compressed;
            }
            set
            {
                this.m_Compressed = value;
            }
        }

        public override bool Connected
        {
            get
            {
                if (this._socket == null)
                {
                    return false;
                }
                return this._socket.Connected;
            }
        }

        public int SendNoResponseInterval
        {
            get
            {
                return this.sendnoresponseinterval;
            }
            set
            {
                this.sendnoresponseinterval = value;
            }
        }

        public bool SSL
        {
            get
            {
                return this.m_SSL;
            }
            set
            {
                this.m_SSL = value;
            }
        }

        public override bool SupportsStartTls
        {
            get
            {
                return true;
            }
        }
    }
}
